from machine import Pin, RTC, Timer
from neopixel import NeoPixel 
from config import setConfig, getConfig
import dht
import network
import random
import time
import gc
import _thread
import ujson
from umqtt.simple import MQTTClient
import ds1302
import jhx_ws2812b as jws
import jhx_net as jnet
try:
    import urequests as requests
except ImportError:
    import requests



ds = ds1302.DS1302(Pin(5),Pin(18),Pin(19))
t_h = dht.DHT11(Pin(12))
wsTemp = Pin(25, Pin.OUT)
np = NeoPixel(wsTemp,256)
rtc = RTC()
# 配置网络连接定时器，1h触发一次
networktimer = Timer(0)
# 配置临时定时器，10s触发一次
mqttPubTimer = Timer(1)


# 创建8*32的二维数组
sourceArr = [[[0, 0, 0]] * 32 for i in range(8)]
wlanConnectFlag = False     # wifi连接状态标志
serverConnectFlag = False   # 服务器连接状态标志
wlan = None
wlanIp = ''
funcMode = 'date1'
temp = '0'
humi = '0'
button = Pin(2, Pin.IN, Pin.PULL_DOWN)

# 可在线更改的相关配置
textColor = [91, 252, 91] # 字体颜色
dateColorChange = True
brightnessChangeFlag = False
brightness = 0.1


# mqtt功能相关配置
client_id = "xbclock_9573"
mserver = '47.98.230.234'
topic_ctl = 'iot/xbclock/ctl'
topic_pub = 'iot/xbclock/pub'
stw = False
client = None
mserverConnectFlag = False

def mqtt_callback(topic, msg):
    """
    收到订阅消息回调
    """
    global client, funcMode, stw, dateColorChange, brightnessChangeFlag, textColor, colorChangeFlag, brightness
    # print('topic: ' + topic.decode())
    # print('msg: ' + msg.decode())
    if topic.decode() == topic_ctl:
        ctlJson = ujson.loads(msg.decode())
        # print(ctlJson)
        if ctlJson['hardwareId'] == 'jhx-001':
            if ctlJson['command'] == 'brightness':
                jws.setBrightness(float(ctlJson['value']))
                brightnessChangeFlag = True
                brightness = float(ctlJson['value'])
            elif ctlJson['command'] == 'color':
                colorItem = ujson.loads(ctlJson['value'])
                textColor = [colorItem['r'], colorItem['g'], colorItem['b']]
            elif ctlJson['command'] == 'dateMode':
                if ctlJson['value'] == 'true':
                    dateColorChange = True
                else:
                    dateColorChange = False
            elif ctlJson['command'] == 'funcMode':
                pass
            elif ctlJson['command'] == 'message':
                stw = True
                jws.cleanScreen(sourceArr, np)
                jws.scrollStr(sourceArr, np, ctlJson['value'], textColor)
                stw = False
            setConfig({
                'funcMode': funcMode,
                'textColor': textColor,
                'dateColorChange': dateColorChange,
                'brightness':  brightness,
            })


# testtimer.init(period=1000 * 10, mode=Timer.PERIODIC, callback=testTimerHandler)

def mqttPubTimerHandler(mqttPubTimer):
    global client, mserverConnectFlag
    if mserverConnectFlag == True:
        client.publish(b"iot/xbclock/pub", b"i'm alive")
        print('推送消息到iot/xbclock/pub')


def mqttThread():
    global client, mserverConnectFlag
    while wlanConnectFlag == False:
        pass
    while True:
        try:
            client = MQTTClient(client_id, mserver, 0, 'admin', '2104157jhx', 30)
            client.set_callback(mqtt_callback)
            client.connect()
            client.subscribe(topic_ctl)
            # client.publish(topic_pub, '{code}', retain=True)
            print("Connected to %s, subscribed to %s topic" % (mserver, topic_ctl))
            mserverConnectFlag = True
            while wlanConnectFlag == True:
                client.wait_msg()
        except OSError as exc:
            print('catch a OSError')
            print(exc)
        finally:
            if client is not None:
                print('off line')
                client.disconnect()
                mserverConnectFlag = False
    gc.collect()    # Free the memory space requested by the thread


def changeFunc(button):
    global funcMode, textColor, dateColorChange, brightness
    if funcMode == 'date1':
        funcMode = 'date2'
    elif funcMode == 'date2':
        funcMode = 'temp'
    elif funcMode == 'temp':
        funcMode = 'humi'
    else:
        funcMode = 'date1'
    setConfig({
                'funcMode': funcMode,
                'textColor': textColor,
                'dateColorChange': dateColorChange,
                'brightness':  brightness,
            })


# 显示日期功能(显示时:分)
def displayDate1():
    global funcMode, sourceArr, np
    jws.cleanScreen(sourceArr, np)
    dateCounter = 10
    weekdayColor = [random.randint(1, 255), random.randint(1, 255), random.randint(1, 255)]
    while funcMode == 'date1':
        while stw == True:
            pass
        if dateCounter % 10 == 0:
            # 时间处理
            dateTup = rtc.datetime()
            dateStr = str(dateTup[4]) + ' ' + (('0' + str(dateTup[5])) if dateTup[5] < 10 else str(dateTup[5]))
            timeHour = str(dateTup[4])
            timeMin = ('0' + str(dateTup[5])) if dateTup[5] < 10 else str(dateTup[5])
            # 上午10点钟整需要清空一下屏幕
            if(dateStr == '10 00'):
                jws.cleanArr(sourceArr, 8, 32, [0, 0])
            # 时间显示位置偏移量
            timeOffset = 2 if dateTup[4] < 10 else 0
            if(dateTup[4] >= 10):
                jws.strMapper(sourceArr, timeHour, [1, 8 - timeOffset], textColor)
            else:
                jws.strMapper(sourceArr, timeHour, [1, 12 - timeOffset], textColor)
            jws.strMapper(sourceArr, timeMin, [1, 18 - timeOffset], textColor)
            if dateTup[6] % 2 == 0:
                jws.charMapper(sourceArr, ':', [1, 16 - timeOffset], textColor)
            else:
                jws.charMapper(sourceArr, ' ', [1, 16 - timeOffset], [0, 0, 0])
            # 日期处理
            weekCounter = 0
            counter = 2
            weekdayColor = jws.colorControl(weekdayColor)
            weekdayTextColor = jws.colorControl(textColor)
            while counter < 30:
                if dateTup[3] == weekCounter:
                    sourceArr[7][counter] = weekdayColor
                    sourceArr[7][counter+1] = weekdayColor
                    sourceArr[7][counter+2] = weekdayColor
                    sourceArr[7][counter+3] = weekdayColor
                    counter += 4
                else:
                    sourceArr[7][counter] = weekdayTextColor
                    sourceArr[7][counter+1] = weekdayTextColor
                    sourceArr[7][counter+2] = weekdayTextColor
                    counter += 3
                counter += 1
                weekCounter += 1
            jws.arrConvert(sourceArr, np)
            np.write()
        dateCounter += 1
        if dateCounter > 6000:
            dateCounter = 1
        time.sleep_ms(100)


# 显示日期功能(显示时:分:秒)
def displayDate2():
    global funcMode, sourceArr, np
    # r, g, b = random.randint(1, 255), random.randint(1, 255), random.randint(1, 255)
    r, g, b = textColor
    jws.cleanScreen(sourceArr, np)
    dateCounter = 10
    rSign = 1
    gSign = 1
    bSign = 1
    while funcMode == 'date2':
        while stw == True:
            pass
        dateTup = rtc.datetime()
        dateStr = str(dateTup[4]) + ':' + (('0' + str(dateTup[5])) if dateTup[5] < 10 else str(dateTup[5])) + ':' + (('0' + str(dateTup[6])) if dateTup[6] < 10 else str(dateTup[6]))
        # print(dateStr)
        # 上午10点钟整需要清空一下屏幕
        if(dateStr == '10:00:00'):
            jws.cleanArr(sourceArr, 8, 32, [0, 0])
        if dateColorChange == True:
            r += random.randint(1, 5) * rSign
            g += random.randint(1, 5) * gSign
            b += random.randint(1, 5) * bSign
            if r < 0 or r > 255:
                r = 1 if r < 0 else 255
                rSign *= -1
            if g < 0 or g > 255:
                g = 1 if g < 0 else 255
                gSign *= -1
            if b < 0 or b > 255:
                b = 1 if b < 0 else 255
                bSign *= -1
            jws.strMapper(sourceArr, dateStr, [1, 3 if dateTup[4]>=10 else 5], [r, g, b])
        else:
            jws.strMapper(sourceArr, dateStr, [1, 3 if dateTup[4]>=10 else 5], textColor)
        jws.arrConvert(sourceArr, np)
        np.write()
        

# 显示温度功能
def displayTemp():
    global funcMode, sourceArr, np, t_h, temp, textColor, brightnessChangeFlag
    tempCounter = 1
    if 0 != 0:
        t_h.measure()
        jws.cleanScreen(sourceArr, np)
        jws.showPattern(sourceArr, np, 'temp-out', [0, 2], [30,144,255], 1)
        jws.showPattern(sourceArr, np, 'temp-in', [2, 3], [255, 0, 0], 0)
        jws.displayStr(sourceArr, np, temp + '*', [2, 15], textColor)
        while funcMode == 'temp':
            while stw == True:
                pass
            if brightnessChangeFlag == True:
                jws.showPattern(sourceArr, np, 'temp-out', [0, 2], [30,144,255], 1)
                jws.showPattern(sourceArr, np, 'temp-in', [2, 3], [255, 0, 0], 0)
                jws.displayStr(sourceArr, np, temp + '*', [2, 15], textColor)
                brightnessChangeFlag = False
            if tempCounter % 36000 == 0:
                t_h.measure()
                tempStr = str(t_h.temperature()) + '*'
            jws.displayStr(sourceArr, np, tempStr, [2, 15], textColor)
            tempCounter += 1
            if tempCounter > 600000:
                tempCounter = 1
            time.sleep_ms(100)
    else :
        if wlanConnectFlag == True:
            response = requests.get('http://weatherapi.market.xiaomi.com/wtr-v2/temp/realtime?cityId=101190101')
            weatherJson = response.json()['weatherinfo']
            temp = weatherJson['temp']
            response.close()
            jws.cleanScreen(sourceArr, np)
            jws.showPattern(sourceArr, np, 'temp-out', [0, 2], [30,144,255], 1)
            jws.showPattern(sourceArr, np, 'temp-in', [2, 3], [255, 0, 0], 0)
            jws.displayStr(sourceArr, np, temp + '*', [2, 15], textColor)
            while funcMode == 'temp':
                while stw == True:
                    pass
                if brightnessChangeFlag == True:
                    jws.showPattern(sourceArr, np, 'temp-out', [0, 2], [30,144,255], 1)
                    jws.showPattern(sourceArr, np, 'temp-in', [2, 3], [255, 0, 0], 0)
                    jws.displayStr(sourceArr, np, temp + '*', [2, 15], textColor)
                    brightnessChangeFlag = False
                if tempCounter % 36000 == 0:
                    response = requests.get('http://weatherapi.market.xiaomi.com/wtr-v2/temp/realtime?cityId=101190101')
                    weatherJson = response.json()['weatherinfo']
                    temp = weatherJson['temp']
                    response.close()
                jws.displayStr(sourceArr, np, temp + '*', [2, 15], textColor)
                tempCounter += 1
                if tempCounter > 600000:
                    tempCounter = 1
                time.sleep_ms(100)
        else:
            jws.displayStr(sourceArr, np, 'net error', [2, 15], textColor)
      

# 显示湿度功能
def displayHumi():
    global funcMode, sourceArr, np, t_h, humi, textColor, brightnessChangeFlag
    humiCounter = 1
    if 0 != 0:
        t_h.measure()
        jws.cleanScreen(sourceArr, np)
        jws.displayStr(sourceArr, np, str(t_h.humidity()) + '%', [2, 15], textColor)
        while funcMode == 'humi':
            while stw == True:
                pass
            if brightnessChangeFlag == True:
                    jws.showPattern(sourceArr, np, 'humi', [0, 2], [30,144,255], 1)
                    brightnessChangeFlag = False
            if humiCounter % 36000 == 0:
                t_h.measure()
                humiStr = str(t_h.humidity()) + '%'
            jws.displayStr(sourceArr, np, humiStr, [2, 15], [30,144,255])
            humiCounter += 1
            if humiCounter > 600000:
                humiCounter = 1
            time.sleep_ms(100)
    else :
        if wlanConnectFlag == True:
            response = requests.get('http://weatherapi.market.xiaomi.com/wtr-v2/temp/realtime?cityId=101190101')
            weatherJson = response.json()['weatherinfo']
            humi = weatherJson['SD']
            response.close()
            jws.cleanScreen(sourceArr, np)
            jws.showPattern(sourceArr, np, 'humi', [0, 2], [30,144,255], 1)
            jws.displayStr(sourceArr, np, humi, [2, 15], textColor)
            while funcMode == 'humi':
                while stw == True:
                    pass
                if brightnessChangeFlag == True:
                    jws.showPattern(sourceArr, np, 'humi', [0, 2], [30,144,255], 1)
                    brightnessChangeFlag = False
                if humiCounter % 36000 == 0:
                    response = requests.get('http://weatherapi.market.xiaomi.com/wtr-v2/temp/realtime?cityId=101190101')
                    weatherJson = response.json()['weatherinfo']
                    humi = weatherJson['SD']
                    response.close()
                jws.displayStr(sourceArr, np, humi, [2, 15], textColor)
                humiCounter += 1
                if humiCounter > 600000:
                    humiCounter = 1
                time.sleep_ms(100)
        else:
            jws.displayStr(sourceArr, np, 'net error', [2, 15], textColor)


# 显示连接动画
def displayConnecting():
    jws.cleanScreen(sourceArr, np)
    jws.showPattern(sourceArr, np, 'wifi', [0, 6], [30,144,255], 1)
    time.sleep_ms(1000)
    # jws.showPattern(sourceArr, np, 'wifi-waiting', [0, 16], [30,144,255], 1)
    jws.showPoint(sourceArr, np, [7, 20], [30,144,255])
    time.sleep_ms(200)
    jws.showPoint(sourceArr, np, [7, 22], [30,144,255])
    time.sleep_ms(200)
    jws.showPoint(sourceArr, np, [7, 24], [30,144,255])
    time.sleep_ms(200)


# 显示连接成功动画
def displayConnected():
#   jws.showPattern(sourceArr, np, 'wifi-connect', [0, 20], [0,255,0], 1)
    jws.cleanArr(sourceArr, 8, 5, [0, 20])
    jws.showPoint(sourceArr, np, [4, 20], [0,255,0])
    time.sleep_ms(100)
    jws.showPoint(sourceArr, np, [5, 21], [0,255,0])
    time.sleep_ms(100)
    jws.showPoint(sourceArr, np, [6, 22], [0,255,0])
    time.sleep_ms(100)
    jws.showPoint(sourceArr, np, [5, 23], [0,255,0])
    time.sleep_ms(100)
    jws.showPoint(sourceArr, np, [4, 24], [0,255,0])
    time.sleep_ms(100)
    jws.showPoint(sourceArr, np, [3, 25], [0,255,0])
    time.sleep_ms(100)
    jws.showPoint(sourceArr, np, [2, 26], [0,255,0])
    time.sleep_ms(100)


# 显示连接失败动画
def displayConnectFailed():
  jws.showPattern(sourceArr, np, 'wifi-fail', [0, 20], [255,0,0], 1)


# 连接网络功能
def networkConnect():
    global sourceArr, np, wlan, wlanIp, wlanConnectFlag
    displayConnecting() # 显示连接中动画
    time.sleep(1)
    wlan = jnet.connectWifi()
    if wlan == None:
        jws.showPattern(sourceArr, np, 'wifi-fail', [0, 20], [255,0,0], 1) # 显示连接失败动画
        time.sleep(1)
    else:
        displayConnected() # 显示连接成功动画
        wlanIp = wlan.ifconfig()[0]   #获取IP地址
        wlanConnectFlag = True
        time.sleep(1)
        # jws.scrollStr(sourceArr, np, 'wlan_ip: ' + str(wlan_ip), [17, 48, 17])
        # 连接服务器xxx


networkTimerCounter = 0
# 时间校准定时器回调函数
def networkTimerHandler(networktimer):
    global networkTimerCounter, rtc, ds
    if networkTimerCounter % 7:
        # 每7小时校准一次esp32系统时间，如果联网的话通过网络校准，否则通过ds1302模块校准
        if wlanConnectFlag == True:
            jnet.sync_ntp()
        else:
            rtc.datetime((ds.year(), ds.month(), ds.day(), ds.weekday(), ds.hour(), ds.minute(), ds.second(), 0))
    # 每24小时对ds1302模块进行一次时间校准，没联网就算了
    if networkTimerCounter % 24 and wlanConnectFlag == True:
        jnet.sync_ntp()
        dateTime = rtc.datetime()
        ds.date_time([dateTime[0], dateTime[1], dateTime[2], (dateTime[3]+1), dateTime[4], dateTime[5], dateTime[6]])
    networkTimerCounter += 1
    if networkTimerCounter > 100:
        networkTimerCounter = 0
    print(networkTimerCounter)


# 日期初始化
def dateInit():
    global rtc, ds, wlanConnectFlag
    print('run there')
    if wlanConnectFlag == True:
        print('run here')
        jnet.sync_ntp()
        dateTime = rtc.datetime()
        # print(dateTime)
        ds.date_time([dateTime[0], dateTime[1], dateTime[2], (dateTime[3]+1), dateTime[4], dateTime[5], dateTime[6]])
        # ds.date_time([2021, 11, 28, 5, 9, 59, 45])
        # rtc.datetime((ds.year(), ds.month(), ds.day(), ds.weekday(), ds.hour(), ds.minute(), ds.second(), 0))
    else:
        rtc.datetime((ds.year(), ds.month(), ds.day(), ds.weekday(), ds.hour(), ds.minute(), ds.second(), 0))


# 加载配置文件
def configInit():
    global funcMode, textColor, dateColorChange
    configDict = getConfig()
    funcMode = configDict['funcMode']
    textColor = configDict['textColor']
    dateColorChange = configDict['dateColorChange']
    brightness = configDict['brightness']
    jws.setBrightness(configDict['brightness'])


def testTimerHandler(testtimer):
    global funcMode
    if funcMode == 'date':
        funcMode = 'temp'
    elif funcMode == 'temp':
        funcMode = 'humi'
    else:
        funcMode = 'date'


def main():
    global sourceArr, np, funcMode
    configInit()
    jws.bootAnimation(sourceArr, np)
    time.sleep(1)
    networkConnect()
    dateInit()
    networktimer.init(period=1000 * 60 * 60, mode=Timer.PERIODIC, callback=networkTimerHandler)
    button.irq(trigger=Pin.IRQ_FALLING, handler=changeFunc)
    _thread.start_new_thread(mqttThread, ())
    # testtimer.init(period=1000 * 10, mode=Timer.PERIODIC, callback=testTimerHandler)
    mqttPubTimer.init(period=1000 * 15, mode=Timer.PERIODIC, callback=mqttPubTimerHandler)
    while True:
        if funcMode == 'date1':
            displayDate1()
        elif funcMode == 'date2':
            displayDate2()
        elif funcMode == 'temp':
            displayTemp()
        elif funcMode == 'humi':
            displayHumi()


# if __name__=='__main__':
#     main()
main()
# displayConnecting()
# displayConnected()
# displayDate()
# displayTemp()
# displayHumi()

